package org.icop.matcher;

import java.util.Collection;
import java.util.List;
import java.util.Set;

import org.icop.frame.IMatcher;
import org.icop.frame.boost.Booster;
import org.icop.frame.eval.Evaluator;
import org.icop.frame.search.Searcher;
import org.icop.frame.select.Selector;
import org.icop.model.MultiSet;
import org.icop.model.ValuedCorrespondence;
import org.jbpt.graph.abs.IEdge;
import org.jbpt.graph.abs.IGraph;
import org.jbpt.hypergraph.abs.IVertex;

/**
 * Implements the actual matching algorithm.
 */
public abstract class AbstractMatcher<G extends IGraph<E,V>, E extends IEdge<V>, V extends IVertex> implements IMatcher<G, E, V> {
	
	private List<Searcher<G, E, V>> searchers = null;
	private List<Booster<G, E, V>> boosters = null;
	private Evaluator<G, E, V> evaluator = null;
	private Selector<G, E, V> selector = null;
	
	private MultiSet<ValuedCorrespondence<V>> viableMappings;
	
		
	/**
	 * Initializes the matcher to perform the given searchers, boosters, evaluators and selectors.
	 * The values must not be null. The list of boosters can be empty, but the list of searchers
	 * should not be empty. (If the list of searchers is empty, the empty mapping will be returned.) 
	 */
	protected void setupMatcher(List<Searcher<G, E, V>> searchers, List<Booster<G, E, V>> boosters, Evaluator<G, E, V> evaluator, Selector<G, E, V> selector){
		setSearchers(searchers);
		setBoosters(boosters);
		setEvaluator(evaluator);
		setSelector(selector);
	}
	
	public Set<ValuedCorrespondence<V>> match(G sg1, G sg2){
		return this.match(sg1,sg2,sg1.getVertices(),sg2.getVertices());
	}
	
	/**
	 * Matches the nodes of the given graphs. It does this by:
	 * 1. performing the searchers in the order in which they appear in 
	 *    the list with which the matcher was initialized, to return
	 *    a set of 'viable' matches;
	 * 2. performing the boosters in the order in which they appear, to 
	 *    'improve' the set of 'viable' matches;
	 * 3. performing the selector to select the 'best' combination of the 
	 *    'viable' matches that are returned by the searchers.
	 * Which combination of 'viable' matches is considered 'best' is determined
	 * by the evaluator.   
	 *  
	 * @param sg1 Graph to determine matches for.
	 * @param sg2 Graph to determine matches for.
	 * @param nodesInSg1 Nodes of the first graph that should be considered for matching
	 * @param nodesInSg2 Nodes of the second graph that should be considered for matching
	 * @return Set of best matches, such that for each (s1,s2) \in mapping: (s1,s2) \subseteq sg1.getVertices() \times sg2.getVertices().
	 */
	public Set<ValuedCorrespondence<V>> match(G sg1, G sg2, Collection<V> nodesInSg1, Collection<V> nodesInSg2) {
		this.viableMappings = new MultiSet<>();
		for (Searcher<G, E, V> s: this.searchers){
			this.viableMappings.addAll(s.search(sg1, sg2, nodesInSg1, nodesInSg2, this.viableMappings));
		}
		for (Booster<G, E, V> b: this.boosters){
			this.viableMappings = b.boost(sg1, sg2, this.viableMappings);
		}
		return this.selector.select(sg1,sg2,this.evaluator,this.viableMappings);
		
	}

	
	public List<Searcher<G, E, V>> getSearchers() {
		return searchers;
	}
	protected void setSearchers(List<Searcher<G, E, V>> searchers) {
		this.searchers = searchers;
	}
	public Evaluator<G, E, V> getEvaluator() {
		return evaluator;
	}
	protected void setEvaluator(Evaluator<G, E, V> evaluator) {
		this.evaluator = evaluator;
	}
	public Selector<G, E, V> getSelector() {
		return selector;
	}
	protected void setSelector(Selector<G, E, V> selector) {
		this.selector = selector;
	}
	public List<Booster<G, E, V>> getBoosters() {
		return boosters;
	}
	protected void setBoosters(List<Booster<G, E, V>> boosters) {
		this.boosters = boosters;
	}

	public MultiSet<ValuedCorrespondence<V>> getViableMappings() {
		return viableMappings;
	}
}
